package cn.qzzg.util;

import com.alibaba.fastjson.JSONObject;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.BoundSetOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * Redis工具类
 */
@SuppressWarnings(value = {"unchecked", "rawtypes"})
@Component
public class RedisUtil {
    @Resource
    RedisTemplate redisTemplate;

    /**
     * 设置有效时间
     *
     * @param key     键
     * @param timeout 超时时间
     * @return true or false
     */
    public boolean expire(String key, long timeout) {
        return expire(key, timeout, TimeUnit.SECONDS);
    }

    /**
     * 设置有效时间
     *
     * @param key      键
     * @param timeout  超时时间
     * @param timeUnit 时间单位
     * @return true or false
     */
    public boolean expire(String key, long timeout, TimeUnit timeUnit) {
        return Boolean.TRUE.equals(redisTemplate.expire(key, timeout, timeUnit));
    }

    /**
     * 获得基本对象列表
     *
     * @param pattern 字符串前缀
     * @return 对象列表
     */
    public Collection<String> keys(String pattern) {
        return redisTemplate.keys(pattern);
    }

    /**
     * 获得缓存到期时间
     *
     * @param key Redis键
     * @return 剩余时间(秒)
     */
    public Long getExpirationTime(String key) {
        return redisTemplate.boundHashOps(key).getOperations().getExpire(TimeUnit.MILLISECONDS);
    }

    /**
     * 删除单个对象
     *
     * @param key 键
     * @return true or false
     */
    public boolean del(String key) {
        return Boolean.TRUE.equals(redisTemplate.delete(key));
    }

    /**
     * 删除集合对象
     *
     * @param collection 对象列表
     */
    public long del(Collection collection) {
        return redisTemplate.delete(collection);
    }

    /**
     * 删除Hash中field键的数据
     *
     * @param key   键
     * @param field field键
     */
    public void delHash(String key, String field) {
        redisTemplate.boundHashOps(key).delete(field);
    }

    /**
     * 缓存基本的对象，Integer、String、实体类等
     *
     * @param key   键
     * @param value 值
     */
    public <T> void setValue(String key, T value) {
        redisTemplate.boundValueOps(key).set(value);
    }

    /**
     * 缓存基本的对象，Integer、String、实体类等
     *
     * @param key      键
     * @param value    值
     * @param timeout  时间
     * @param timeUnit 时间单位
     */
    public <T> void setValue(String key, T value, Integer timeout, TimeUnit timeUnit) {
        redisTemplate.boundValueOps(key).set(value, timeout, timeUnit);
    }

    /**
     * 获得缓存基本对象。
     *
     * @param key 键
     * @return 缓存键对应的数据
     */
    public <T> T getValue(String key) {
        return (T) redisTemplate.boundValueOps(key);
    }

    /**
     * 往Hash中存入数据
     *
     * @param key   键
     * @param field field键
     * @param value 值
     */
    public <T> void setHash(String key, String field, T value) {
        redisTemplate.boundHashOps(key).put(field, value);
    }

    /**
     * 设置带过期时间的hash中field键
     *
     * @param key      键
     * @param field    field键
     * @param value    值
     * @param expire   过期时间
     * @param timeUnit 时间单位
     */
    public <T> void setHash(String key, String field, T value, Long expire, TimeUnit timeUnit) {
        RedisExpireData redisData = new RedisExpireData(value, timeUnit.toMillis(expire));
        redisTemplate.boundHashOps(key).put(field, JSONObject.toJSONString(redisData));
    }

    /**
     * 往hash中存入一个map对象集合
     *
     * @param key  键
     * @param data 数据对象
     */
    public <T> void setHash(String key, Map<String, T> data) {
        if (data != null) redisTemplate.boundHashOps(key).putAll(data);
    }

    /**
     * 获取hash对象对应的field键值
     *
     * @param key   键
     * @param field field键
     */
    public <T> T getHash(String key, String field) {
        BoundHashOperations boundHashOperations = redisTemplate.boundHashOps(key);
        Object hashValue = boundHashOperations.get(field);
        if (hashValue == null) return null;
        RedisExpireData redisData = JSONObject.parseObject(hashValue.toString(), RedisExpireData.class);
        if (redisData != null) {
            Object obj = redisData.getStoreData();
            if (obj == null)
                redisTemplate.boundHashOps(key).delete(field);
            return (T) obj;
        } else {
            return (T) hashValue;
        }
    }


    /**
     * 获得Hash
     *
     * @param key 键
     * @return 缓存键对应的数据
     */
    public <T> Map<String, T> getHash(String key) {
        return redisTemplate.boundHashOps(key).entries();
    }

    /**
     * 获取包含多个field键值的List对象
     *
     * @param key    键
     * @param fields fields键集合
     * @return 缓存键对应的数据
     */
    public <T> List<T> getHash(String key, Collection<Object> fields) {
        return redisTemplate.boundHashOps(key).multiGet(fields);
    }


    /**
     * 缓存List数据
     *
     * @param key  键值
     * @param data 数据对象
     * @return 对象
     */
    public <T> long setList(String key, List<T> data) {
        Long count = redisTemplate.boundListOps(key).rightPushAll(data);
        return count == null ? 0 : count;
    }

    /**
     * 获得list对象
     *
     * @param key 键
     * @return 缓存键对应的数据
     */
    public <T> List<T> getList(String key) {
        return redisTemplate.boundListOps(key).range(0, -1);
    }

    /**
     * 缓存Set
     *
     * @param key  键
     * @param data 数据对象
     * @return 缓存数据的对象
     */
    public <T> BoundSetOperations<String, T> setSet(String key, Set<T> data) {
        BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key);
        for (T t : data)
            setOperation.add(t);
        return setOperation;
    }

    /**
     * 获得set对象
     *
     * @param key 键
     * @return 缓存键对应的数据
     */
    public <T> Set<T> getSet(String key) {
        return redisTemplate.boundSetOps(key).members();
    }

    @NoArgsConstructor
    @AllArgsConstructor
    @Data
    private static class RedisExpireData implements Serializable {
        private Object storeData;
        private Long expire;
        private Long dataTime;

        public RedisExpireData(Object data, Long expire) {
            this.storeData = data;
            this.expire = expire;
            this.dataTime = System.currentTimeMillis();
        }

        /**
         * 获取对应的expire数据
         *
         * @return value or null
         */
        public Object getStoreData() {
            Long curr = System.currentTimeMillis();
            if (curr - this.dataTime > this.expire) {
                return null;
            } else {
                return this.storeData;
            }
        }
    }
}